/*
 * Copyright (C) 2010 Graham Allan
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.strath.cis.grallan.findbugs.adaptiveranking.arm;

import java.io.Serializable;
import java.util.Comparator;

import edu.strath.cis.grallan.findbugs.adaptiveranking.AbstractAdaptiveRanker;
import edu.strath.cis.grallan.findbugs.adaptiveranking.AdaptiveRankedBugList;
import edu.strath.cis.grallan.findbugs.adaptiveranking.IRankScheme;
import edu.strath.cis.grallan.findbugs.adaptiveranking.gui.sort.DefaultSortOrder;
import edu.strath.cis.grallan.findbugs.adaptiveranking.population.PopulationExtractor;
import edu.strath.cis.grallan.findbugs.adaptiveranking.util.BugFromAdaptiveRankComparator;
import edu.strath.cis.grallan.findbugs.adaptiveranking.util.NumberRankScheme;
import edu.strath.cis.grallan.findbugs.adaptiveranking.util.StackedComparatorBuilder;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.gui2.IApplicationState;

/**
 * @author Graham Allan (grallan@cis.strath.ac.uk)
 */
public class ARMRanker extends AbstractAdaptiveRanker<ARMRank> {

	protected IRankScheme<Double> rankScheme;

	private UserDesignationToARMRankMap designationToRankMapper = new UserDesignationToARMRankMap();
	private final Comparator<ARMRank> tieBreakingComparator;
	private final ARMRankCalculator rankCalculator = new ARMRankCalculator();

	public ARMRanker(IApplicationState applicationState, PopulationExtractor populationExtractor) {
		super(applicationState, populationExtractor);
		this.rankedList = new AdaptiveRankedBugList<ARMRank>();
		this.rankScheme = new NumberRankScheme<Double>(-1.0d, 1.0d, 0.0d);
		this.tieBreakingComparator = new StackedComparatorBuilder<ARMRank>().sortBy(RANK_COMPARATOR).then(
				BugFromAdaptiveRankComparator.transformFor(DefaultSortOrder.COMPARATOR)).thenTie();
	}

	/**
	 * Orders by the value of rank.
	 */
	public static class RankValueComparator implements Comparator<ARMRank>, Serializable {
		public int compare(ARMRank first, ARMRank second) {
			return Double.compare(first.rank(), second.rank());
		}
	}

	private static final RankValueComparator RANK_COMPARATOR = new RankValueComparator();

	@Override
	protected void assignRankForBug(PopulationExtractor extractor, BugInstance bug) {
		double armRank = 0.0d;
		if (bugAlreadyClassified(bug)) {
			armRank = designationToRankMapper.rankFor(bug);
			assignBugARMRank(bug, armRank);
			return;
		}

		armRank = rankCalculator.calculateRank(extractor, bug);

		assignBugARMRank(bug, armRank);
	}

	private void assignBugARMRank(BugInstance bug, double armRank) {
		super.assignBugRank(new ARMRank(bug, armRank));
	}

	@Override
	protected void createRankScheme() {
		this.rankScheme = new NumberRankScheme<Double>(-1.0d, 1.0d, 0.0d);

	}

	@Override
	protected Comparator<? super ARMRank> rankingComparator() {
		return tieBreakingComparator;
	}

	@Override
	protected double relativePositionOf(ARMRank bugRank) {
		throwExceptionIfRankSchemeHasNotBeenCreated();
		return rankScheme.relativePosition(bugRank.rank());
	}

	public double defaultRelativeRank() {
		throwExceptionIfRankSchemeHasNotBeenCreated();
		return rankScheme.relativePosition(rankScheme.getInitial());
	}

	private void throwExceptionIfRankSchemeHasNotBeenCreated() {
		if (rankScheme == null)
			throw new NullPointerException("RankScheme should have been created.");
	}

}
